/*
 * Feed.java
 *
 * Created on July 16, 2007, 12:06 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.atomojo.app.client;

import java.net.URI;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Collection;
import java.util.Collections;
import org.infoset.xml.Child;
import org.infoset.xml.Document;
import org.infoset.xml.Element;
import org.infoset.xml.InfosetFactory;
import org.infoset.xml.Item;
import org.infoset.xml.ItemConstructor;
import org.infoset.xml.ItemDestination;
import org.infoset.xml.XMLException;
import org.infoset.xml.util.DocumentSource;

/**
 *
 * @author alex
 */
public class Feed extends CategorizedXMLEntity
{
   Map<String,Entry> byId;
   List<Entry> byOrder;
   Map<URI,List<Entry>> byCategory;
   
   /** Creates a new instance of Feed */
   public Feed(Document doc)
      throws XMLException
   {
      this(doc,false,true);
   }
   
   public Feed(Document doc,boolean ordered,boolean loadEntries)
      throws XMLException
   {
      super(doc);
      byId = new TreeMap<String,Entry>();
      byOrder = ordered ? new ArrayList<Entry>() : null;
      byCategory = null;
      init(loadEntries);
   }
   
   public Feed()
   {
      super(null);
      byId = new TreeMap<String,Entry>();
      byOrder = null;
      byCategory = null;
      try {
         doc = InfosetFactory.getDefaultInfoset().createItemConstructor().createDocument();
         doc.createDocumentElement(XML.FEED_NAME);
      } catch (XMLException ex) {
         // THis should only happen if the infoset is misconfigured
         throw new RuntimeException("Cannot create feed document.",ex);
      }
      index();
   }
   
   protected void init(boolean loadEntries) 
      throws XMLException
   {
      if (loadEntries) {
         Iterator<Element> entries = doc.getDocumentElement().getElementsByName(XML.ENTRY_NAME);
         ItemConstructor constructor = InfosetFactory.getDefaultInfoset().createItemConstructor();
         while (entries.hasNext()) {
            Document entryDoc = constructor.createDocument(doc.getBaseURI());
            entryDoc.add((Child)entries.next().copyOfItem(true));
            add(new Entry(entryDoc));
         }
      }
   }
   
   public void add(Entry entry)
   {
      String id = entry.getId();
      byId.put(id,entry);
      if (byOrder!=null) {
         byOrder.add(entry);
      }
   }
   
   public Collection<String> getEntryIds() {
      return byId.keySet();
   }
   
   public Collection<Entry> getEntries() {
      return byOrder==null ? byId.values() : byOrder;
   }
   
   public void index() {
      super.index();
      byCategory = new TreeMap<URI,List<Entry>>();
      for (Entry entry : byId.values()) {
         entry.index();
         for (Term term : entry.getTerms().values()) {
            List<Entry> entries = byCategory.get(term.getURI());
            if (entries==null) {
               entries = new ArrayList<Entry>();
               byCategory.put(term.getURI(),entries);
            }
            entries.add(entry);
         }
      }
      
   }
   
   public Collection<Entry> getEntriesByTerm(URI term)
   {
      if (byCategory==null) {
         Collection<Entry> empty = Collections.emptyList();
         return empty;
      } else {
         Collection<Entry> entries = byCategory.get(term);
         if (entries==null) {
            entries = Collections.emptyList();
         }
         return entries;
      }
   }
   
   public void generate(final ItemDestination dest) 
      throws XMLException
   {
      if (doc==null) {
         throw new XMLException("The feed instance was not instantiated using a document.");
      }
      ItemDestination top = new ItemDestination() {
         int level = 0;
         public void send(Item item)
            throws XMLException
         {
            switch (item.getType()) {
               case DocumentItem:
                  level++;
                  break;
               case ElementItem:
                  level++;
                  break;
               case ElementEndItem:
                  if (level==2) {
                     for (Entry entry : byId.values()) {
                        DocumentSource.generate(entry.getDocument().getDocumentElement(),false,dest);
                     }
                  }
            }

            dest.send(item);
            
            switch (item.getType()) {
               case DocumentEndItem:
                  level--;
                  break;
               case ElementEndItem:
                  level--;
                  break;
            }
         }
      };
      DocumentSource.generate(doc, false, top);
   }
   
}
